package com.diy.sigmund.diycommon.spring.advice;

import com.diy.sigmund.diycommon.spring.anno.SecurityParameter;
import com.diy.sigmund.diycommon.spring.config.CustomCommonConfig;
import com.diy.sigmund.diycommon.util.encrypt.AesEncryptUtil;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

/**
 * 请求数据解密
 *
 * 原文使用DES加解密，这里做了优化，使用AES加解密
 * https://www.jianshu.com/p/760b284fc269
 *
 * @author ylm-sigmund
 * @since 2021/8/17 15:33
 */
//@ControllerAdvice(basePackages = "com.sansence.house.controller")
@ControllerAdvice
public class MyRequestBodyAdvice implements RequestBodyAdvice {

    @Autowired
    private CustomCommonConfig customCommonConfig;
    private static final Logger log = LoggerFactory.getLogger(MyRequestBodyAdvice.class);

    @Override
    public boolean supports(MethodParameter methodParameter, Type type,
            Class<? extends HttpMessageConverter<?>> aClass) {
        log.info("MyRequestBodyAdvice supports");
        final boolean encryptEnabled = customCommonConfig.isEncryptEnabled();
        // 加解密配置为false时，不走加解密
        if (!encryptEnabled) {
            return false;
        }
        final Method method = methodParameter.getMethod();
        // 判断方法不为空
        if (Objects.isNull(method)) {
            return false;
        }
        // 判断方法上是否有该注解
        if (!method.isAnnotationPresent(SecurityParameter.class)) {
            return false;
        }
        final SecurityParameter securityParameter = methodParameter.getMethodAnnotation(SecurityParameter.class);
        return securityParameter.inDecode();
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter,
            Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return body;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type type,
            Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        try {
            log.info("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密");
            return new MyHttpInputMessage(inputMessage);
        } catch (Exception e) {
            log.error("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密出现异常：", e);
            return inputMessage;
        }
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter,
            Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return body;
    }

    static class MyHttpInputMessage implements HttpInputMessage {

        private HttpHeaders headers;

        private InputStream body;

        MyHttpInputMessage(HttpInputMessage inputMessage) throws Exception {
            this.headers = inputMessage.getHeaders();
            final String data = IOUtils.toString(inputMessage.getBody(), StandardCharsets.UTF_8.toString());
            log.info("解密前的数据是{}", data);
            this.body = IOUtils.toInputStream(AesEncryptUtil.decrypt(data), StandardCharsets.UTF_8.toString());
        }

        @Override
        public InputStream getBody() throws IOException {
            return body;
        }

        @Override
        public HttpHeaders getHeaders() {
            return headers;
        }

    }
}
